package br.com.infocruz.seam.crm.util;

import java.lang.reflect.*;
import java.util.*;

@SuppressWarnings("unchecked")
public class GenericTypeResolver {
	
	public static Class resolveTypeArgument(Class classToIntrospect) {
		Class[] typeArguments = resolveTypeArguments(classToIntrospect);
		if(typeArguments !=null && typeArguments.length > 0){
			return typeArguments[0];	
		}
		return null;
	}

	public static Class[] resolveTypeArguments(Class classToIntrospect) {
		List<Class> resolvedTypes = new ArrayList<Class>();
		resolvedTypes.addAll(Arrays.asList(resolveInterfaceTypeArguments(classToIntrospect)));
		resolvedTypes.addAll(Arrays.asList(resolveSuperclassTypeArguments(classToIntrospect)));
		return resolvedTypes.toArray(new Class[0]);
	}	
	
	public static Class resolveInterfaceTypeArgument(Class classToIntrospect) {
		Class[] typeArguments = resolveInterfaceTypeArguments(classToIntrospect);
		if(typeArguments !=null && typeArguments.length > 0){
			return typeArguments[0];	
		}
		return null;
	}

	public static Class[] resolveInterfaceTypeArguments(Class classToIntrospect) {
		List<Class> resolvedTypes = new ArrayList<Class>();
		Type[] types = classToIntrospect.getGenericInterfaces();
		for (Type type : types) {
			resolvedTypes.add(doResolveTypeArguments(type)[0]);
		}
		return resolvedTypes.toArray(new Class[0]);
	}
	
	public static Class resolveSuperclassTypeArgument(Class classToIntrospect) {
		Class[] typeArguments = resolveSuperclassTypeArguments(classToIntrospect);
		if(typeArguments !=null && typeArguments.length > 0){
			return typeArguments[0];	
		}
		return null;		
	}

	public static Class[] resolveSuperclassTypeArguments(Class classToIntrospect) {
		Type type = classToIntrospect.getGenericSuperclass();
		return doResolveTypeArguments(type);
	}

	private static Class[] doResolveTypeArguments(Type genericType) {
		List<Class> result = new ArrayList<Class>();
		if (genericType instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) genericType;
			@SuppressWarnings("unused")
			Type rawType = parameterizedType.getRawType();
			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
			for (Type type : actualTypeArguments) {
				result.add((Class)type);
			}
			return result.toArray(new Class[0]);
		}
		return null;
	}
}
